﻿
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using ServiceStack.Redis;
using ServiceStack.Text;

namespace TestRedis.Helper
{
    public class RedisCacheHelper
    {
        private RedisCacheHelper()
        {
        }

        private static string ConnectionString
        {
            get
            {
                return ConfigurationManager.ConnectionStrings["RedisCacheConnection"] == null
                    ? ""
                    : ConfigurationManager.ConnectionStrings["RedisCacheConnection"].ConnectionString;
            }
        }

        private static RedisCacheHelper _redisCacheClient;

        private static readonly Dictionary<string, PooledRedisClientManager> ReadClientManagerDict =
            new Dictionary<string, PooledRedisClientManager>();

        private static readonly Dictionary<string, PooledRedisClientManager> WriteClientManagerDict =
            new Dictionary<string, PooledRedisClientManager>();

        private const int DefaultTimeout = 5000;

        private static readonly object MLock = new object();

        public static RedisCacheHelper Instance()
        {
            if (_redisCacheClient != null) return _redisCacheClient;
            lock (MLock)
            {
                if (_redisCacheClient != null) return _redisCacheClient;

                // ReSharper disable once PossibleMultipleWriteAccessInDoubleCheckLocking
                _redisCacheClient = new RedisCacheHelper();

                return Init(ConnectionString);
            }
        }

        private static RedisCacheHelper Init(string connectionString)
        {
            if (string.IsNullOrEmpty(connectionString))
                return _redisCacheClient;
            var redisConnectionMatch = MatchConnectionString(connectionString);
            var password = redisConnectionMatch.Groups["pwd"].Value;

            var connectionStrings = redisConnectionMatch.Groups["hosts"].Value.Split(new[] { ';', ',' },
                StringSplitOptions.RemoveEmptyEntries);
            if (!string.IsNullOrEmpty(password))
            {
                connectionStrings = connectionStrings.Select(p => string.Format("{0}@{1}", password, p)).ToArray();
            }

            if (!WriteClientManagerDict.ContainsKey(connectionString))
            {
                var writeClientManager =
                    CreatePooledRedisClientManager(connectionStrings.Length == 1
                        ? connectionStrings
                        : connectionStrings.Take(1).ToArray());
                WriteClientManagerDict.Add(connectionString, writeClientManager);
            }

            if (!ReadClientManagerDict.ContainsKey(connectionString))
            {
                var readClientManager =
                    CreatePooledRedisClientManager(connectionStrings);

                ReadClientManagerDict.Add(connectionString, readClientManager);
            }


            return _redisCacheClient;
        }

        private static Match MatchConnectionString(string connectionString)
        {
            //var connectionString = ConfigurationManager.ConnectionStrings["RedisCacheConnection"].ConnectionString;
            var redisConnectionReg = new Regex(@"redis://(?<pwd>.*?)@(?<hosts>.+)", RegexOptions.IgnoreCase);
            var redisConnectionMatch = redisConnectionReg.Match(connectionString);
            if (string.IsNullOrEmpty(connectionString)
                || !redisConnectionMatch.Success)
                throw new Exception("Redis 连接串配置错误");

            return redisConnectionMatch;
        }

        private static PooledRedisClientManager CreatePooledRedisClientManager(params string[] connectionStrings)
        {
            return new PooledRedisClientManager(
                connectionStrings,
                connectionStrings,
                new RedisClientManagerConfig()
                {
                    AutoStart = true,
                    MaxReadPoolSize = 30,
                    MaxWritePoolSize = 30
                })
            {
                ConnectTimeout = DefaultTimeout,
                SocketReceiveTimeout = DefaultTimeout,
                SocketSendTimeout = DefaultTimeout
            };
        }

        /// <summary>
        /// 从链接池中获取Redis链接
        /// </summary>
        /// <returns></returns>
        private static IRedisClient GetReadRedisClient(string connectionString)
        {
            IRedisClient client;
            if (ReadClientManagerDict.ContainsKey(connectionString))
            {
                client = ReadClientManagerDict[connectionString].GetClient();
            }
            else
            {
                lock (MLock)
                {
                    Init(connectionString);
                }

                client = ReadClientManagerDict[connectionString].GetClient();
            }

            return client;
        }

        /// <summary>
        /// 从链接池中获取Redis链接
        /// </summary>
        /// <returns></returns>
        private static IRedisClient GetWriteRedisClient(string connectionString)
        {
        GetWriteClient:
            IRedisClient client = null;
            if (WriteClientManagerDict.ContainsKey(connectionString))
            {
                try
                {
                    client = WriteClientManagerDict[connectionString].GetClient();
                }
                catch (Exception ex)
                {
                    //LogHelper.Error("Redis服务器无法连接", ex);
                }
            }
            else
            {
                lock (MLock)
                {
                    Init(connectionString);
                }
                client = WriteClientManagerDict[connectionString].GetClient();
            }
            try
            {
                if (client != null && client.Info["role"] == "master") return client;
            }
            catch (Exception)
            {
                // ignored
            }
            var redisConnectionMatch = MatchConnectionString(connectionString);
            var connections = redisConnectionMatch.Groups["hosts"].Value.Split(new[] { ';', ',' },
                StringSplitOptions.RemoveEmptyEntries);
            //只有一个客户端并且客户端无法连接
            if (connections.Length == 1)
                return client;
            var password = redisConnectionMatch.Groups["pwd"].Value;
            lock (MLock)
            {

                var masterHost = string.Empty;
                try
                {
                    var clientReadonly = GetReadRedisClient(connectionString);
                    masterHost = clientReadonly.Info["role"] == "master"
                        ? clientReadonly.Host
                        : clientReadonly.Info["master_host"];
                    if (!string.IsNullOrEmpty(password))
                        masterHost = password + "@" + masterHost;
                }
                catch (Exception ex)
                {
                    //LogHelper.Error("Redis服务器无法连接", ex);
                }
                WriteClientManagerDict[connectionString] = CreatePooledRedisClientManager(masterHost);
                goto GetWriteClient;
            }
        }

        /// <summary>
        /// 生成MD5的Key
        /// </summary>
        /// <param name="parms"></param>
        /// <returns></returns>
        //public static string GeneralKey(params string[] parms)
        //{
        //    return UtilityClass.EncryptWebFormMd5(string.Join("_", parms));
        //}

        /// <summary>
        /// 统计指定队列长度
        /// </summary>
        /// <param name="listId"></param>
        public int LLen(string listId)
        {
            return LLen(ConnectionString, listId);
        }

        /// <summary>
        /// 统计指定队列长度
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="listId"></param>
        public int LLen(string connectionString, string listId)
        {
            using (var client = GetReadRedisClient(connectionString))
            {
                return client.GetListCount(listId);
            }
        }


        /// <summary>
        /// 加入到指定队列
        /// </summary>
        /// <param name="listId"></param>
        /// <param name="vals"></param>
        public void EnQueue(string listId, params string[] vals)
        {
            EnQueue(ConnectionString, listId, vals);
        }

        /// <summary>
        /// 加入到指定队列
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="listId"></param>
        /// <param name="vals"></param>
        public void EnQueue(string connectionString, string listId, params string[] vals)
        {
            if (vals == null
                || vals.Length == 0)
                return;
            using (var client = GetWriteRedisClient(connectionString))
            {
                foreach (var val in vals)
                {
                    client.EnqueueItemOnList(listId, val);
                }
            }
        }

        /// <summary>
        /// 从指定队列取出
        /// </summary>
        /// <param name="listId"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public string[] DeQueue(string listId, int count)
        {
            return DeQueue(ConnectionString, listId, count);
        }


        /// <summary>
        /// 从指定队列取出
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="listId"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public string[] DeQueue(string connectionString, string listId, int count)
        {
            if (count <= 0) return null;
            using (var client = GetWriteRedisClient(connectionString))
            {
                var records = new List<string>();

                for (var i = 0; i < count; i++)
                {
                    var tmp = client.DequeueItemFromList(listId);
                    if (string.IsNullOrWhiteSpace(tmp))
                        break;
                    records.Add(tmp);
                }
                return records.ToArray();
            }
        }

        /// <summary>
        /// 设置缓存，成功返回true,如果换成失败或者已经存在则返回false
        /// </summary>
        /// <typeparam name="T">放入缓存的实体类型</typeparam>
        /// <param name="key">缓存的KEY</param>
        /// <param name="val">缓存的对象</param>
        /// <param name="expiredAt">设置过期时间</param>
        /// <returns>设置结果 bool</returns>
        public bool Set<T>(string key, T val, DateTime? expiredAt = null)
        {
            return Set(ConnectionString, key, val, expiredAt);
        }

        /// <summary>
        /// 设置缓存，成功返回true,如果换成失败或者已经存在则返回false
        /// </summary>
        /// <typeparam name="T">放入缓存的实体类型</typeparam>
        /// <param name="connectionString"></param>
        /// <param name="key">缓存的KEY</param>
        /// <param name="val">缓存的对象</param>
        /// <param name="expiredAt">设置过期时间</param>
        /// <returns>设置结果 bool</returns>
        public bool Set<T>(string connectionString, string key, T val, DateTime? expiredAt = null)
        {
            using (var client = GetWriteRedisClient(connectionString))
            {
                if (expiredAt.HasValue)
                    return client.Set(key, val, expiredAt.Value);
                return client.Set(key, val);
            }
        }

        /// <summary>
        /// 移除缓存对象
        /// </summary>
        /// <param name="key">缓存的KEY</param>
        /// <returns>移除结果</returns>
        public bool Remove(string key)
        {
            return Remove(ConnectionString, key);
        }

        /// <summary>
        /// 移除缓存对象
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="key">缓存的KEY</param>
        /// <returns>移除结果</returns>
        public bool Remove(string connectionString, string key)
        {
            using (var client = GetWriteRedisClient(connectionString))
            {
                return client.Remove(key);
            }
        }

        /// <summary>
        /// 是否包含名称为KEY的缓存对象
        /// </summary>
        /// <param name="key">缓存KEY</param>
        /// <returns>是否存在 bool</returns>
        public bool ContainKey(string key)
        {
            return ContainKey(ConnectionString, key);
        }

        /// <summary>
        /// 是否包含名称为KEY的缓存对象
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="key">缓存KEY</param>
        /// <returns>是否存在 bool</returns>
        public bool ContainKey(string connectionString, string key)
        {
            using (var client = GetReadRedisClient(connectionString))
            {
                return client.ContainsKey(key);
            }
        }

        /// <summary>
        /// 获取指定KEY的缓存对象
        /// </summary>
        /// <typeparam name="T">缓存对象实体类型</typeparam>
        /// <param name="key">缓存KEY</param>
        /// <returns>缓存对象实体 T</returns>
        public T Get<T>(string key)
        {
            return Get<T>(ConnectionString, key);
        }

        /// <summary>
        /// 获取指定KEY的缓存对象
        /// </summary>
        /// <typeparam name="T">缓存对象实体类型</typeparam>
        /// <param name="connectionString"></param>
        /// <param name="key">缓存KEY</param>
        /// <returns>缓存对象实体 T</returns>
        public T Get<T>(string connectionString, string key)
        {
            using (var client = GetReadRedisClient(connectionString))
            {
                return client.Get<T>(key);
            }
        }

        /// <summary>
        /// 获取指定pattern对应的键
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns>键集合</returns>
        public List<string> GetKeys(string pattern)
        {
            return GetKeys(ConnectionString, pattern);
        }

        /// <summary>
        /// 获取指定pattern对应的键
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="pattern"></param>
        /// <returns>键集合</returns>
        public List<string> GetKeys(string connectionString, string pattern)
        {
            using (var client = GetReadRedisClient(connectionString))
            {
                return client.SearchKeys(pattern);
            }
        }

        /// <summary>
        /// 发布信息
        /// </summary>
        /// <param name="toChannel">发布到的频道</param>
        /// <param name="message">发布的内容</param>
        public IRedisSubscription CreateSubscription()
        {
            using (var client = GetReadRedisClient(ConnectionString))
            {
                return client.CreateSubscription();
            }
        }

        /// <summary>
        /// 发布信息
        /// </summary>
        /// <param name="toChannel">发布到的频道</param>
        /// <param name="message">发布的内容</param>
        /// <returns>返回值为发动给了几个订阅者</returns>
        public int Publish(string toChannel, string message)
        {
            using (var client = GetReadRedisClient(ConnectionString))
            {
                Console.WriteLine("Publisher  Channel:{0},Message:{1}", toChannel, message);
                return client.PublishMessage(toChannel, message);
            }
        }

        public long Increment(string key)
        {
            using (var client = GetReadRedisClient(ConnectionString))
            {
                return client.IncrementValue(key);
            }
        }

        public long Increment(string key, int count)
        {
            using (var client = GetReadRedisClient(ConnectionString))
            {
                return client.IncrementValueBy(key, count);
            }
        }

        public long Decrement(string key)
        {
            using (var client = GetReadRedisClient(ConnectionString))
            {
                return client.DecrementValue(key);
            }
        }

        public long Decrement(string key, int count)
        {
            using (var client = GetReadRedisClient(ConnectionString))
            {
                return client.DecrementValueBy(key, count);
            }
        }
    }

    /// <summary>
    /// redis 订阅者
    /// </summary>
    public class RedisSubscriber
    {
        /// <summary>
        /// 订阅
        /// </summary>
        public static void Subscription()
        {
            using (RedisClient consumer = new RedisClient("192.168.210.36", 6379))
            {
                //创建订阅
                IRedisSubscription subscriber = consumer.CreateSubscription();

                //接收消息处理Action
                subscriber.OnMessage = (channel, msg) =>
                {
                    Console.WriteLine("频道【" + channel + "】订阅客户端接收消息：" + ":" + msg + "         [" +
                                      DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "]");
                    Console.WriteLine("订阅数：" + subscriber.SubscriptionCount);
                    Console.WriteLine("___________________________________________________________________");

                };

                //订阅事件处理
                subscriber.OnSubscribe = (channel) =>
                {
                    Console.WriteLine("订阅客户端：开始订阅" + channel);
                };

                //取消订阅事件处理
                subscriber.OnUnSubscribe = (a) => { Console.WriteLine("订阅客户端：取消订阅"); };

                //订阅频道
                subscriber.SubscribeToChannels("channel-1");
            }

        }

    }

}
